﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Speech.Recognition;
using System.Speech.Synthesis;
using SharedModule;
using log4net;
using Newtonsoft.Json;
using WebCommon;
using SuperWebSocket;
using SuperSocket.SocketBase;
using System.Web;
using System.Collections;
using System.Globalization;
using System.Threading;

namespace 语音识别
{
    class Program
    {
        static MockSocketServer server;
        static ILog log = LogManager.GetLogger("Mock-Console");
        static bool startFlag = false;
        static void Main(string[] args)
        {
            log4net.Config.XmlConfigurator.Configure();
            server = MockSocketServerHub.createServer("BUILT-IN");
            server.Start();
            server.AddCustomMessageHandler(appServer_NewMessageReceived);
            server.AddCustomDataHandler(appServer_NewDataReceived);
            server.AddCustomSessionConnectHandler(appServer_NewSessionConnected);
            server.AddCustomSessionCloseHandler(appServer_NewSessionDisconnected);
            Console.ReadLine();
            //// Create an in-process speech recognizer for the en-US locale.
            //using ( SpeechRecognitionEngine recognizer = new SpeechRecognitionEngine(
            //    new System.Globalization.CultureInfo("zh-CN")))
            //    {
            //        Choices choices = new Choices(new string[] { "你好","8月10日到9月10日","木头人",
            //        "我是","没有","建材","深铁","有道","深铁大屏"});
            //        GrammarBuilder gb = new GrammarBuilder(choices);

            //        // Create and load a dictation grammar.  
            //        recognizer.LoadGrammar(new Grammar(gb));

            //        // Add a handler for the speech recognized event.  
            //        recognizer.SpeechRecognized +=
            //          new EventHandler<SpeechRecognizedEventArgs>(recognizer_SpeechRecognized);

            //        // Configure input to the speech recognizer.  
            //        recognizer.SetInputToDefaultAudioDevice();

            //        // Start asynchronous, continuous speech recognition.  
            //        recognizer.RecognizeAsync(RecognizeMode.Multiple);
            //        Console.WriteLine("启动成功");
            //        // Keep the console window open.  
            //        while (true)
            //        {
            //            Console.ReadLine();
            //        }
            //    }
        }

        // Handle the SpeechRecognized event.  
        static void recognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e, WebSocketSession session, Message clientMessage)
        {
            Console.WriteLine("已识别: " + e.Result.Text);
            SpeechSynthesizer ssh = new SpeechSynthesizer()
            {
                Volume = 80,
                Rate = 0
            };
            ssh.SelectVoiceByHints(VoiceGender.Female, VoiceAge.Child);

            ssh.SelectVoice("Microsoft Huihui Desktop");
            // ssh.SetOutputToWaveFile("1.wav");
            //ssh.SpeakAsync("收到," + e.Result.Text);
            if (e.Result.Text == "识别开始")
            {
                startFlag = true;
            }
            else if (e.Result.Text == "识别结束")
            {
                startFlag = false;
            }

            if (startFlag)
            {
                var mo = GenerateNormalMessage(session.SessionID, e.Result.Text, clientMessage.RequestId);
                session.Send(JsonConvert.SerializeObject(mo));
            }
        }

        public static void appServer_NewMessageReceived(WebSocketSession session, string message)
        {
            try
            {
                dynamic obj = JsonConvert.DeserializeObject(message);
                Message clientMessage = new Message()
                {
                    Type = obj.type,
                    Source = obj.source,
                    SessionIds = obj.sessionIds == null ? null : obj.sessionIds.ToObject<List<string>>(),
                    Url = obj.url,
                    RequestId = obj.requestId
                };
                switch (clientMessage.Type)
                {
                    case MessageType.MESSAGE:
                        OnMessage(session, clientMessage.Source, clientMessage.RequestId);
                        break;
                    case MessageType.BROADCAST:
                        if (obj.sessionIds == null)
                            OnBroadcast(clientMessage.Source, session.SessionID, clientMessage.RequestId);
                        else
                            OnMessageTo(session, clientMessage.Source, clientMessage.SessionIds, clientMessage.RequestId);
                        break;
                    case MessageType.SUBSCRIBE:
                        if (clientMessage.Source.sql != null && clientMessage.Source.sql == "audio") 
                            Audio(session, clientMessage, obj);
                        else 
                            OnSubscribe(session, clientMessage, obj);
                        break;
                    case MessageType.SUBSCRIBE_CANCEL:
                        OnSubscribeCancel(session, obj, clientMessage.RequestId);
                        break;
                    case MessageType.CLOSE:
                        OnClose(session, obj, clientMessage.RequestId);
                        break;
                    case MessageType.STOP:
                        OnServerStop();
                        break;
                    default:
                        OnMessage(session, clientMessage.Source, clientMessage.RequestId);
                        break;
                }
                log.Info(clientMessage.Source.ToString());
            }
            catch (Exception e)
            {
                log.Error(e.Message);
            }
        }

        public static void appServer_NewDataReceived(WebSocketSession session, byte[] data)
        {
            string dataString = Encoding.UTF8.GetString(data);
            log.Info("appServer_NewDataReceived:" + dataString);
        }

        public static void appServer_NewSessionConnected(WebSocketSession session)
        {
            log.Info("client has been connected, sessionId:" + session.SessionID);
            //var media = WebClientUtil.Get(ConfigsUtil.StorageAPIUrl, "/connected?project=PILOT&token=test").Result;
            //dynamic payload = JsonConvert.DeserializeObject(media);
            MessageObject mo = GenerateNormalMessage(session.SessionID, "client connected", session.SessionID);
            session.Send(JsonConvert.SerializeObject(mo));
        }

        public static void appServer_NewSessionDisconnected(WebSocketSession session, CloseReason reason)
        {
            log.Info("client has been disconnected, sessionId:" + session.SessionID);
        }

        private static void OnMessage(WebSocketSession session, dynamic message, string requestId)
        {
            MessageObject mo = GenerateMessage(session.SessionID, message, requestId);
            var rsp = GeneratePilotMessage(session.SessionID, requestId, "on message");
            session.Send(JsonConvert.SerializeObject(rsp));
        }

        private static void OnMessageTo(WebSocketSession session, dynamic message, List<string> sessionIds, string requestId)
        {
            List<WebSocketSession> sessions = new List<WebSocketSession>();
            foreach (var sessionId in sessionIds)
            {
                sessions.Add(server.GetSessionById(sessionId));
            }

            MessageObject mo = GenerateMessage(session.SessionID, message, requestId);
            server.BroadCast(mo, sessions);
        }

        private static void OnBroadcast(dynamic message, string sessionId, string requestId)
        {
            MessageObject mo = GenerateMessage(sessionId, message, requestId);
            server.BroadCast(mo);
        }

        public static bool TryGetCultureInfo(string cultureCode, string DefaultCultureCode, out CultureInfo culture)
        {
            try
            {
                culture = CultureInfo.GetCultureInfo(cultureCode);
                return true;
            }
            catch (CultureNotFoundException)
            {
                if (DefaultCultureCode == null)
                    culture = CultureInfo.CurrentCulture;
                else
                {
                    try
                    {
                        culture = CultureInfo.GetCultureInfo(DefaultCultureCode);
                    }
                    catch (CultureNotFoundException)
                    {
                        culture = CultureInfo.CurrentCulture;
                    }
                }
            }
            return false;
        }

        private static void Audio(WebSocketSession session, Message clientMessage, dynamic obj)
        {
            Console.WriteLine(" TimerElaspsed And Send Data Once");
            var mo = GenerateNormalMessage(session.SessionID, "dictation start..", clientMessage.RequestId);
            session.Send(JsonConvert.SerializeObject(mo));

            CultureInfo culture;
            bool flag = TryGetCultureInfo("zh-CN", null,out culture);
            Console.WriteLine("=======culture info========");
            Console.WriteLine(CultureInfo.CurrentCulture);

            // Create an in-process speech recognizer for the en-US locale.
            using (SpeechRecognitionEngine recognizer = new SpeechRecognitionEngine(new CultureInfo("zh-CN")))
            {
                Choices choices = new Choices(new string[]
                {
                    "首页","返回", "关闭", "退出",
                    "监控","视频","视频监控","品质监控",
                    "公司基本信息", "公司运营情况","公司大事记", "大事记", "运营情况","基本信息"," 经营范围","运营",
                    "行业指数", "指数","产品检测", "合格率","物流信息","物流","项目信息","项目","供应商信息","供应商","合作伙伴",
                    "识别开始","识别结束"
                });
                GrammarBuilder gb = new GrammarBuilder(choices);

                // Create and load a dictation grammar.
                recognizer.LoadGrammar(new Grammar(gb));

                // Add a handler for the speech recognized event.  
                recognizer.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>((sendor, e) =>
                  recognizer_SpeechRecognized(sendor, e, session, clientMessage)
                );

                // Configure input to the speech recognizer.  
                recognizer.SetInputToDefaultAudioDevice();

                // Start asynchronous, continuous speech recognition.  
                recognizer.RecognizeAsync(RecognizeMode.Multiple);
                Console.WriteLine("启动成功");
                // Keep the console window open.  
                while (true)
                {
                    Console.ReadLine();
                }
            }
        }

        private static void OnSubscribe(WebSocketSession session, Message clientMessage, dynamic obj)
        {
            var interval = obj.interval ?? 5000;
            var duration = obj.duration ?? 5000 * 10;
            SubMessage smsg = new SubMessage()
            {
                Interval = Convert.ToInt32(interval),
                Duration = Convert.ToInt32(duration),
            };
            //while
            if (smsg.Duration > MockConst.MAX_SUBSCRIBE_TIME)
                smsg.Duration = MockConst.MAX_SUBSCRIBE_TIME;
            DateTime end = DateTime.Now.AddSeconds(smsg.Duration);
            SubscribeArgs args = new SubscribeArgs()
            {
                Source = clientMessage.Source,
                CurSession = session,
                RequestId = clientMessage.RequestId,
                ExpireTime = end
            };
            System.Timers.Timer timer = new System.Timers.Timer(smsg.Interval);
            args.CurTimer = timer;

            var cacheKey = MakeSubKey(session.SessionID, clientMessage.RequestId);
            //var cacheKey = clientMessage.RequestId;
            CacheUtility.SetCache(cacheKey, timer);
            timer.Elapsed += new System.Timers.ElapsedEventHandler((sendor, e) => TimerElaspsed(sendor, e, args, cacheKey));
            timer.Enabled = true;
            timer.Start();
        }

        private static string MakeSubKey(string sessionId, dynamic requestId)
        {
            return string.Format("{0}_{1}", sessionId, requestId);
        }

        private static void TimerElaspsed(object sendor, System.Timers.ElapsedEventArgs e, SubscribeArgs args, string subKey)
        {
            if (DateTime.Now.CompareTo(args.ExpireTime) > 0)
            {
                Console.WriteLine("TimerElaspsed End , Will Be Canceled");
                CancelSubscription(args.CurTimer, args.CurSession, "Subscription complete", subKey, args.RequestId);
            }
            else
            {
                Console.WriteLine(" TimerElaspsed And Send Data Once");
                var mo = GenerageRandomSubMessage(args.CurSession, args.Source, args.RequestId, subKey);
                args.CurSession.Send(JsonConvert.SerializeObject(mo));
            }
        }

        private static MessageObject GenerageRandomSubMessage(WebSocketSession session, dynamic msg, string requestId, string subKey)
        {
            var cacheSubDatasKey = MakeSubCacheDataKey(subKey);

            var cachedDatas = CacheUtility.GetCache(cacheSubDatasKey);

            MessageObject mo = GenerateMessage(session.SessionID, msg, requestId);

            if (!(cachedDatas is Array) || ((Array)cachedDatas).Length == 0)
            {
                string project = HttpUtility.UrlEncode(msg.project.ToString());
                string sql = HttpUtility.UrlEncode(msg.sql.ToString());
                //string querystring = string.Format("?project={0}&sql={1}", project, sql);
                //var media = WebClientUtil.Get(ConfigsUtil.StorageAPIUrl, querystring).Result;
                //cachedDatas = JsonConvert.DeserializeObject(JsonConvert.DeserializeObject(media) as string ?? string.Empty);
                //if (cachedDatas != null)
                //{
                //    CacheUtility.SetCache(cacheSubDatasKey, cachedDatas);
                //}
                cachedDatas = new string[] { "TEST VALUE" };
            }

            dynamic msgPayloads = null;
            if (cachedDatas is IEnumerable)
            {
                var count = ((IEnumerable<object>)cachedDatas).Count();
                var randomCount = new Random().Next(1, count + 1);
                msgPayloads = ((IEnumerable<object>)cachedDatas).ToList().TakeWhile((item, index) =>
                {
                    return index % randomCount == 0;
                });
            }
            else
            {
                msgPayloads = cachedDatas;
            }

            var rsp = GeneratePilotMessage(session.SessionID, requestId, msgPayloads);
            return rsp;
        }

        public static void OnSubscribeCancel(WebSocketSession session, dynamic obj, string requestId)
        {
            var subKey = MakeSubKey(session.SessionID, requestId);
            System.Timers.Timer timer = CacheUtility.GetCache(subKey) as System.Timers.Timer;
            CancelSubscription(timer, session, "Subscription has been cancelled, sessionId - " + session.SessionID, subKey, requestId);
        }

        private static void OnClose(WebSocketSession session, dynamic obj, string requestId)
        {
            foreach (string key in CacheUtility.ShowAllCache())
            {
                if (key.StartsWith(session.SessionID) && !key.EndsWith("_DATAS"))
                {
                    System.Timers.Timer timer = CacheUtility.GetCache(session.SessionID) as System.Timers.Timer;
                    CancelSubscription(timer, session, "Session has been closed, sessionId - " + session.SessionID, key, requestId);
                }
            }

            session.Close();
        }

        private static void OnServerStop()
        {
            server.Stop();
            log.Info("Server stopped");
        }

        private static void CancelSubscription(System.Timers.Timer timer, WebSocketSession session, string msg, string subKey, string requestId)
        {
            timer.Stop();
            timer.Dispose();
            timer = null;

            CacheUtility.RemoveOneCache(subKey);
            CacheUtility.RemoveOneCache(MakeSubCacheDataKey(subKey));
            log.Info(msg);

            MessageObject mo = GenerateMessage(session.SessionID, msg, requestId);
            session.Send(JsonConvert.SerializeObject(mo));
            Console.WriteLine(subKey + "  Canceled");
        }

        private static string MakeSubCacheDataKey(string subKey)
        {
            return string.Format("{0}_DATAS", subKey);
        }

        public static MessageObject GenerateNormalMessage(string sessionID, string msg, string requestId)
        {
            return new MessageObject()
            {
                SessionId = sessionID,
                Data = msg,
                RequestId = requestId
            };
        }

        public static MessageObject GenerateMessage(string sessionID, dynamic msg, string requestId)
        {
            if (msg != null && msg.GetType() == typeof(string))
            {
                try
                {
                    msg = JsonConvert.DeserializeObject(msg);
                }
                catch (Exception ex)
                {
                    log.Error(ex.Message);
                }
            }

            dynamic data = null;
            try
            {
                if (msg != null && msg.data != null)
                {
                    data = msg.data;
                }
                data = msg;
            }
            catch (Exception)
            {
                data = msg;
            }
            return new MessageObject()
            {
                SessionId = sessionID,
                Data = data ?? new Object[] { },
                RequestId = requestId
            };
        }


        public static MessageObject GeneratePilotMessage(string sessionID, dynamic requestId, dynamic msg)
        {
            if (msg != null && msg.GetType() == typeof(string))
            {
                try
                {
                    msg = JsonConvert.DeserializeObject(msg);
                }
                catch (Exception ex)
                {
                    log.Error(ex.Message);
                }
            }
            dynamic data = new Object[] { };
            try
            {
                data = msg.data ?? msg.Data ?? msg;
            }
            catch (Exception)
            {
                data = msg;
            }
            return new PilotMessageObject()
            {
                SessionId = sessionID,
                Data = data ?? new Object[] { },
                ErrorCode = 0,
                ErrorMsg = null,
                RequestId = requestId
            };
        }
    }
}

public class PilotMessageObject : MessageObject
{

    [JsonProperty("requestId")]
    public string RequestId { get; set; }

    [JsonProperty("errorCode")]
    public int ErrorCode { get; set; }

    [JsonProperty("errorMsg")]
    public string ErrorMsg { get; set; }
}